require 'csv'

module IfItWerent4AR
  module FileOp
    # writes/updates/erase objects from storage file
    DELIMITER = "\t"

    module_function

    def storage_path(klass)
      "db/#{klass.name.underscore}_storage.csv"
    end

    def write_to_storage(object)
      if find_in_storage(object)
        raise "Record with given id already exists".inspect
      else
        write(object)
      end
    end

    def update_in_storage(object)
      found_object = find_in_storage(object)
      return false unless found_object
      found_object.serialize == object.serialize ? object : rewrite(object)
    end

    def find_in_storage(object)
      IfItWerent4AR::Validations::validate_primary_key_presence(object.id)
      found_object = nil
      serialized   = nil
      CSV.foreach(self.storage_path(object.class), col_sep: DELIMITER, headers: true) do |csv_row|
        if csv_row['id'].to_i == object.id
          serialized = csv_row.fields.to_s
          break
        end
      end
      serialized ? object.class.deserialize(serialized) : false
    end

    def find_in_storage_by(attrs = {}, klass)
      attrs.stringify_keys!
      found_records = []
      CSV.foreach(self.storage_path(klass), col_sep: DELIMITER, headers: true) do |csv_row|
        match = true
        attrs.each_pair do |attr, value|
          if value.to_s != csv_row[attr]
            match = false
            break
          end
        end
        found_records << klass.deserialize(csv_row.fields.to_s) if match
      end
      found_records
    end

    def destroy_from_storage(object)
      find_in_storage(object) ? erase(object) : false
    end

    private

    module_function

    def write(object)
      CSV.open(self.storage_path(object.class), 'ab', col_sep: DELIMITER, headers: true) do |csv|
        csv << eval(object.serialize)
      end
    end

    def rewrite(object)
      delete_line(object)
      write(object)
    end

    def erase(object)
      delete_line(object)
    end

    def delete_line(object)
      is_deleted = false
      headers = nil
      storage = File.open(self.storage_path(object.class), 'r+')
      storage.each do |line|
        if headers
          line_csv = CSV.parse(line, col_sep: DELIMITER)
          if line_csv[0][0].to_i == object.id
            # seek back to the beginning of the line
            storage.seek(-line.length, IO::SEEK_CUR)
            # overwrite line with spaces and add a newline char
            storage.write(' ' * (line.length - 1))
            is_deleted = true
            break
          end
        end
        headers ||= line
      end
      storage.close
      is_deleted
    end
  end
end
